// For a given PUT path, create all intermediate subdirectories
// for given path. Return 0 if the path itself is a directory,
// or -1 on error, 1 if OK.
static int put_dir(const char *path) {
    char buf[PATH_MAX];
    const char *s, *p;
    struct mgstat st;
    size_t len;

    for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) {
        len = p - path;
        assert(len < sizeof(buf));
        (void) memcpy(buf, path, len);
        buf[len] = '\0';

        // Try to create intermediate directory
        if (mg_stat(buf, &st) == -1 && mg_mkdir(buf, 0755) != 0) {
            return -1;
        }

        // Is path itself a directory?
        if (p[1] == '\0') {
            return 0;
        }
    }

    return 1;
}

static void put_file(struct mg_connection *conn, const char *path) {
    struct mgstat st;
    const char *range;
    int64_t r1, r2;
    FILE *fp;
    int rc;

    conn->request_info.status_code = mg_stat(path, &st) == 0 ? 200 : 201;

    if ((rc = put_dir(path)) == 0) {
        mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n", conn->request_info.status_code);
    } else if (rc == -1) {
        send_http_error(conn, 500, http_500_error,
                "put_dir(%s): %s", path, strerror(ERRNO));
    } else if ((fp = mg_fopen(path, "wb+")) == NULL) {
        send_http_error(conn, 500, http_500_error,
                "fopen(%s): %s", path, strerror(ERRNO));
    } else {
        set_close_on_exec(fileno(fp));
        range = mg_get_header(conn, "Content-Range");
        r1 = r2 = 0;
        if (range != NULL && parse_range_header(range, &r1, &r2) > 0) {
            conn->request_info.status_code = 206;
            // TODO(lsm): handle seek error
            (void) fseeko(fp, (off_t) r1, SEEK_SET);
        }
        if (forward_body_data(conn, fp, INVALID_SOCKET, NULL))
            (void) mg_printf(conn, "HTTP/1.1 %d OK\r\n\r\n",
                    conn->request_info.status_code);
        (void) fclose(fp);
    }
}

static void send_ssi_file(struct mg_connection *, const char *, FILE *, int);

static void do_ssi_include(struct mg_connection *conn, const char *ssi,
        char *tag, int include_level) {
    char file_name[BUFSIZ], path[PATH_MAX], *p;
    struct vec root;
    int is_ssi;
    FILE *fp;

    get_document_root(conn, &root);

    // sscanf() is safe here, since send_ssi_file() also uses buffer
    // of size BUFSIZ to get the tag. So strlen(tag) is always < BUFSIZ.
    if (sscanf(tag, " virtual=\"%[^\"]\"", file_name) == 1) {
        // File name is relative to the webserver root
        (void) mg_snprintf(conn, path, sizeof(path), "%.*s%c%s",
                root.len, root.ptr, DIRSEP, file_name);
    } else if (sscanf(tag, " file=\"%[^\"]\"", file_name) == 1) {
        // File name is relative to the webserver working directory
        // or it is absolute system path
        (void) mg_snprintf(conn, path, sizeof(path), "%s", file_name);
    } else if (sscanf(tag, " \"%[^\"]\"", file_name) == 1) {
        // File name is relative to the currect document
        (void) mg_snprintf(conn, path, sizeof(path), "%s", ssi);
        if ((p = strrchr(path, DIRSEP)) != NULL) {
            p[1] = '\0';
        }
        (void) mg_snprintf(conn, path + strlen(path),
                sizeof(path) - strlen(path), "%s", file_name);
    } else {
        cry(conn, "Bad SSI #include: [%s]", tag);
        return;
    }

    if ((fp = mg_fopen(path, "rb")) == NULL) {
        cry(conn, "Cannot open SSI #include: [%s]: fopen(%s): %s",
                tag, path, strerror(ERRNO));
    } else {
        set_close_on_exec(fileno(fp));
        is_ssi = match_extension(path, conn->ctx->config[SSI_EXTENSIONS]);
        if (is_ssi) {
            send_ssi_file(conn, path, fp, include_level + 1);
        } else {
            send_file_data(conn, fp, INT64_MAX);
        }
        (void) fclose(fp);
    }
}

#if !defined(NO_POPEN)
static void do_ssi_exec(struct mg_connection *conn, char *tag) {
    char cmd[BUFSIZ];
    FILE *fp;

    if (sscanf(tag, " \"%[^\"]\"", cmd) != 1) {
        cry(conn, "Bad SSI #exec: [%s]", tag);
    } else if ((fp = popen(cmd, "r")) == NULL) {
        cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO));
    } else {
        send_file_data(conn, fp, INT64_MAX);
        (void) pclose(fp);
    }
}
#endif // !NO_POPEN

static void send_ssi_file(struct mg_connection *conn, const char *path,
        FILE *fp, int include_level) {
    char buf[BUFSIZ];
    int ch, len, in_ssi_tag;

    if (include_level > 10) {
        cry(conn, "SSI #include level is too deep (%s)", path);
        return;
    }

    in_ssi_tag = 0;
    len = 0;

    while ((ch = fgetc(fp)) != EOF) {
        if (in_ssi_tag && ch == '>') {
            in_ssi_tag = 0;
            buf[len++] = (char) ch;
            buf[len] = '\0';
            assert(len <= (int) sizeof(buf));
            if (len < 6 || memcmp(buf, "<!--#", 5) != 0) {
                // Not an SSI tag, pass it
                (void) mg_write(conn, buf, (size_t)len);
            } else {
                if (!memcmp(buf + 5, "include", 7)) {
                    do_ssi_include(conn, path, buf + 12, include_level);
#if !defined(NO_POPEN)
                } else if (!memcmp(buf + 5, "exec", 4)) {
                    do_ssi_exec(conn, buf + 9);
#endif // !NO_POPEN
                } else {
                    cry(conn, "%s: unknown SSI " "command: \"%s\"", path, buf);
                }
            }
            len = 0;
        } else if (in_ssi_tag) {
            if (len == 5 && memcmp(buf, "<!--#", 5) != 0) {
                // Not an SSI tag
                in_ssi_tag = 0;
            } else if (len == (int) sizeof(buf) - 2) {
                cry(conn, "%s: SSI tag is too large", path);
                len = 0;
            }
            buf[len++] = ch & 0xff;
        } else if (ch == '<') {
            in_ssi_tag = 1;
            if (len > 0) {
                (void) mg_write(conn, buf, (size_t)len);
            }
            len = 0;
            buf[len++] = ch & 0xff;
        } else {
            buf[len++] = ch & 0xff;
            if (len == (int) sizeof(buf)) {
                (void) mg_write(conn, buf, (size_t)len);
                len = 0;
            }
        }
    }

    // Send the rest of buffered data
    if (len > 0) {
        (void) mg_write(conn, buf, (size_t)len);
    }
}

static void handle_ssi_file_request(struct mg_connection *conn,
        const char *path) {
    FILE *fp;

    if ((fp = mg_fopen(path, "rb")) == NULL) {
        send_http_error(conn, 500, http_500_error, "fopen(%s): %s", path,
                strerror(ERRNO));
    } else {
        set_close_on_exec(fileno(fp));
        mg_printf(conn, "HTTP/1.1 200 OK\r\n"
                "Content-Type: text/html\r\nConnection: %s\r\n\r\n",
                suggest_connection_header(conn));
        send_ssi_file(conn, path, fp, 0);
        (void) fclose(fp);
    }
}

// This is the heart of the Mongoose's logic.
// This function is called when the request is read, parsed and validated,
// and Mongoose must decide what action to take: serve a file, or
// a directory, or call embedded function, etcetera.
static void handle_request(struct mg_connection *conn) {
    struct mg_request_info *ri = &conn->request_info;
    char path[PATH_MAX];
    int uri_len;
    struct mgstat st;

    if ((conn->request_info.query_string = strchr(ri->uri, '?')) != NULL) {
        * conn->request_info.query_string++ = '\0';
    }
    uri_len = strlen(ri->uri);
    (void) url_decode(ri->uri, (size_t)uri_len, ri->uri, (size_t)(uri_len + 1), 0);
    remove_double_dots_and_double_slashes(ri->uri);
    convert_uri_to_file_name(conn, ri->uri, path, sizeof(path));

    DEBUG_TRACE(("%s", ri->uri));
    debug("%d的request uri为 %s", conn->client.sock, ri->uri);
    if (!check_authorization(conn, path)) {
        debug("%d send_authorization_request", conn->client.sock);
        send_authorization_request(conn);
    } else if (call_user(conn, MG_NEW_REQUEST) != NULL) {
        // Do nothing, callback has served the request
    } else if (strstr(path, PASSWORDS_FILE_NAME)) {
        // Do not allow to view passwords files
        send_http_error(conn, 403, "Forbidden", "Access Forbidden");
    } else if (conn->ctx->config[DOCUMENT_ROOT] == NULL) {
        send_http_error(conn, 404, "Not Found", "Not Found");
    } else if ((!strcmp(ri->request_method, "PUT") ||
                !strcmp(ri->request_method, "DELETE")) &&
            (conn->ctx->config[PUT_DELETE_PASSWORDS_FILE] == NULL ||
             !is_authorized_for_put(conn))) {
        debug("%d send_authorization_request", conn->client.sock);
        send_authorization_request(conn);
    } else if (!strcmp(ri->request_method, "PUT")) {
        debug("%d开始put file", conn->client.sock);
        put_file(conn, path);
    } else if (!strcmp(ri->request_method, "DELETE")) {
        if (mg_remove(path) == 0) {
            send_http_error(conn, 200, "OK", "");
        } else {
            send_http_error(conn, 500, http_500_error, "remove(%s): %s", path,
                    strerror(ERRNO));
        }
    } else if (mg_stat(path, &st) != 0) {
        send_http_error(conn, 404, "Not Found", "%s", "File not found");
    } else if (st.is_directory && ri->uri[uri_len - 1] != '/') {
        (void) mg_printf(conn,
                "HTTP/1.1 301 Moved Permanently\r\n"
                "Location: %s/\r\n\r\n", ri->uri);
    } else if (st.is_directory &&
            !substitute_index_file(conn, path, sizeof(path), &st)) {
        if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes")) {
            debug("%d开始处理目录请求%s", conn->client.sock, path);
            handle_directory_request(conn, path);
        } else {
            send_http_error(conn, 403, "Directory Listing Denied",
                    "Directory listing denied");
        }
    } else if (match_extension(path, conn->ctx->config[CGI_EXTENSIONS])) {
        if (strcmp(ri->request_method, "POST") &&
                strcmp(ri->request_method, "GET")) {
            send_http_error(conn, 501, "Not Implemented",
                    "Method %s is not implemented", ri->request_method);
        } else {
            debug("%d开始处理cgi请求%s", conn->client.sock, path);
            handle_cgi_request(conn, path);
        }
    } else if (match_extension(path, conn->ctx->config[SSI_EXTENSIONS])) {
        debug("%d开始处理ssi_file请求%s", conn->client.sock, path);
        handle_ssi_file_request(conn, path);
    } else if (is_not_modified(conn, &st)) {
        debug("%d %s Not Modified", conn->client.sock, path);
        send_http_error(conn, 304, "Not Modified", "");
    } else {
        debug("%d开始处理文件请求%s", conn->client.sock, path);
        handle_file_request(conn, path, &st);
    }
}

static void close_all_listening_sockets(struct mg_context *ctx) {
    struct socket *sp, *tmp;
    for (sp = ctx->listening_sockets; sp != NULL; sp = tmp) {
        tmp = sp->next;
        (void) closesocket(sp->sock);
        free(sp);
    }
}

// Valid listening port specification is: [ip_address:]port[s|p]
// Examples: 80, 443s, 127.0.0.1:3128p, 1.2.3.4:8080sp
static int parse_port_string(const struct vec *vec, struct socket *so) {
    struct usa *usa = &so->lsa;
    int a, b, c, d, port, len;

    // MacOS needs that. If we do not zero it, subsequent bind() will fail.
    memset(so, 0, sizeof(*so));

    if (sscanf(vec->ptr, "%d.%d.%d.%d:%d%n", &a, &b, &c, &d, &port, &len) == 5) {
        // IP address to bind to is specified
        usa->u.sin.sin_addr.s_addr = htonl((a << 24) | (b << 16) | (c << 8) | d);
    } else if (sscanf(vec->ptr, "%d%n", &port, &len) == 1) {
        // Only port number is specified. Bind to all addresses
        usa->u.sin.sin_addr.s_addr = htonl(INADDR_ANY);
    } else {
        return 0;
    }
    assert(len > 0 && len <= (int) vec->len);

    if (strchr("sp,", vec->ptr[len]) == NULL) {
        return 0;
    }

    so->is_ssl = vec->ptr[len] == 's';
    so->is_proxy = vec->ptr[len] == 'p';
    usa->len = sizeof(usa->u.sin);
    usa->u.sin.sin_family = AF_INET;
    usa->u.sin.sin_port = htons((uint16_t) port);

    return 1;
}

static int set_ports_option(struct mg_context *ctx) {
    const char *list = ctx->config[LISTENING_PORTS];
    int reuseaddr = 1, success = 1;
    SOCKET sock;
    struct vec vec;
    struct socket so, *listener;

    while (success && (list = next_option(list, &vec, NULL)) != NULL) {
        if (!parse_port_string(&vec, &so)) {
            cry(fc(ctx), "%s: %.*s: invalid port spec. Expecting list of: %s",
                    __func__, vec.len, vec.ptr, "[IP_ADDRESS:]PORT[s|p]");
            success = 0;
        } else if (so.is_ssl && ctx->ssl_ctx == NULL) {
            cry(fc(ctx), "Cannot add SSL socket, is -ssl_cert option set?");
            success = 0;
        } else if ((sock = socket(PF_INET, SOCK_STREAM, 6)) == INVALID_SOCKET ||
#if !defined(_WIN32)
                // On Windows, SO_REUSEADDR is recommended only for
                // broadcast UDP sockets
                setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
                    sizeof(reuseaddr)) != 0 ||
#endif // !_WIN32
                bind(sock, &so.lsa.u.sa, so.lsa.len) != 0 ||
                listen(sock, 20) != 0) {
            closesocket(sock);
            cry(fc(ctx), "%s: cannot bind to %.*s: %s", __func__,
                    vec.len, vec.ptr, strerror(ERRNO));
            success = 0;
        } else if ((listener = (struct socket *)
                    calloc(1, sizeof(*listener))) == NULL) {
            closesocket(sock);
            cry(fc(ctx), "%s: %s", __func__, strerror(ERRNO));
            success = 0;
        } else {
            *listener = so;
            listener->sock = sock;
            set_close_on_exec(listener->sock);
            listener->next = ctx->listening_sockets;
            ctx->listening_sockets = listener;
        }
    }

    if (!success) {
        close_all_listening_sockets(ctx);
    }

    return success;
}

static void log_header(const struct mg_connection *conn, const char *header,
        FILE *fp) {
    const char *header_value;

    if ((header_value = mg_get_header(conn, header)) == NULL) {
        (void) fprintf(fp, "%s", " -");
    } else {
        (void) fprintf(fp, " \"%s\"", header_value);
    }
}

static void log_access(const struct mg_connection *conn) {
    const struct mg_request_info *ri;
    FILE *fp;
    char date[64];

    fp = conn->ctx->config[ACCESS_LOG_FILE] == NULL ?  NULL :
        mg_fopen(conn->ctx->config[ACCESS_LOG_FILE], "a+");

    if (fp == NULL)
        return;

    (void) strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z",
            localtime(&conn->birth_time));

    ri = &conn->request_info;

    flockfile(fp);

    (void) fprintf(fp,
            "%s - %s [%s] \"%s %s HTTP/%s\" %d %" INT64_FMT,
            inet_ntoa(conn->client.rsa.u.sin.sin_addr),
            ri->remote_user == NULL ? "-" : ri->remote_user,
            date,
            ri->request_method ? ri->request_method : "-",
            ri->uri ? ri->uri : "-",
            ri->http_version,
            conn->request_info.status_code, conn->num_bytes_sent);
    log_header(conn, "Referer", fp);
    log_header(conn, "User-Agent", fp);
    (void) fputc('\n', fp);
    (void) fflush(fp);

    funlockfile(fp);
    (void) fclose(fp);
}

static int isbyte(int n) {
    return n >= 0 && n <= 255;
}

// Verify given socket address against the ACL.
// Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed.
static int check_acl(struct mg_context *ctx, const struct usa *usa) {
    int a, b, c, d, n, mask, allowed;
    char flag;
    uint32_t acl_subnet, acl_mask, remote_ip;
    struct vec vec;
    const char *list = ctx->config[ACCESS_CONTROL_LIST];

    if (list == NULL) {
        return 1;
    }

    (void) memcpy(&remote_ip, &usa->u.sin.sin_addr, sizeof(remote_ip));

    // If any ACL is set, deny by default
    allowed = '-';

    while ((list = next_option(list, &vec, NULL)) != NULL) {
        mask = 32;

        if (sscanf(vec.ptr, "%c%d.%d.%d.%d%n", &flag, &a, &b, &c, &d, &n) != 5) {
            cry(fc(ctx), "%s: subnet must be [+|-]x.x.x.x[/x]", __func__);
            return -1;
        } else if (flag != '+' && flag != '-') {
            cry(fc(ctx), "%s: flag must be + or -: [%s]", __func__, vec.ptr);
            return -1;
        } else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {
            cry(fc(ctx), "%s: bad ip address: [%s]", __func__, vec.ptr);
            return -1;
        } else if (sscanf(vec.ptr + n, "/%d", &mask) == 0) {
            // Do nothing, no mask specified
        } else if (mask < 0 || mask > 32) {
            cry(fc(ctx), "%s: bad subnet mask: %d [%s]", __func__, n, vec.ptr);
            return -1;
        }

        acl_subnet = (a << 24) | (b << 16) | (c << 8) | d;
        acl_mask = mask ? 0xffffffffU << (32 - mask) : 0;

        if (acl_subnet == (ntohl(remote_ip) & acl_mask)) {
            allowed = flag;
        }
    }

    return allowed == '+';
}

static void add_to_set(SOCKET fd, fd_set *set, int *max_fd) {
    FD_SET(fd, set);
    if (fd > (SOCKET) *max_fd) {
        *max_fd = (int) fd;
    }
}

#if !defined(_WIN32)
static int set_uid_option(struct mg_context *ctx) {
    struct passwd *pw;
    const char *uid = ctx->config[RUN_AS_USER];
    int success = 0;

    if (uid == NULL) {
        success = 1;
    } else {
        if ((pw = getpwnam(uid)) == NULL) {
            cry(fc(ctx), "%s: unknown user [%s]", __func__, uid);
        } else if (setgid(pw->pw_gid) == -1) {
            cry(fc(ctx), "%s: setgid(%s): %s", __func__, uid, strerror(errno));
        } else if (setuid(pw->pw_uid) == -1) {
            cry(fc(ctx), "%s: setuid(%s): %s", __func__, uid, strerror(errno));
        } else {
            success = 1;
        }
    }

    return success;
}
#endif // !_WIN32

#if !defined(NO_SSL)
static pthread_mutex_t *ssl_mutexes;

static void ssl_locking_callback(int mode, int mutex_num, const char *file,
        int line) {
    line = 0;    // Unused
    file = NULL; // Unused

    if (mode & CRYPTO_LOCK) {
        (void) pthread_mutex_lock(&ssl_mutexes[mutex_num]);
    } else {
        (void) pthread_mutex_unlock(&ssl_mutexes[mutex_num]);
    }
}

static unsigned long ssl_id_callback(void) {
    return (unsigned long) pthread_self();
}

#if !defined(NO_SSL_DL)
static int load_dll(struct mg_context *ctx, const char *dll_name,
        struct ssl_func *sw) {
    union {void *p; void (*fp)(void);} u;
    void  *dll_handle;
    struct ssl_func *fp;

    if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) {
        cry(fc(ctx), "%s: cannot load %s", __func__, dll_name);
        return 0;
    }

    for (fp = sw; fp->name != NULL; fp++) {
#ifdef _WIN32
        // GetProcAddress() returns pointer to function
        u.fp = (void (*)(void)) dlsym(dll_handle, fp->name);
#else
        // dlsym() on UNIX returns void *. ISO C forbids casts of data pointers to
        // function pointers. We need to use a union to make a cast.
        u.p = dlsym(dll_handle, fp->name);
#endif /* _WIN32 */
        if (u.fp == NULL) {
            cry(fc(ctx), "%s: %s: cannot find %s", __func__, dll_name, fp->name);
            return 0;
        } else {
            fp->ptr = u.fp;
        }
    }

    return 1;
}
#endif // NO_SSL_DL

// Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
static int set_ssl_option(struct mg_context *ctx) {
    struct mg_request_info request_info;
    SSL_CTX *CTX;
    int i, size;
    const char *pem = ctx->config[SSL_CERTIFICATE];
    const char *chain = ctx->config[SSL_CHAIN_FILE];

    if (pem == NULL) {
        return 1;
    }

#if !defined(NO_SSL_DL)
    if (!load_dll(ctx, SSL_LIB, ssl_sw) ||
            !load_dll(ctx, CRYPTO_LIB, crypto_sw)) {
        return 0;
    }
#endif // NO_SSL_DL

    // Initialize SSL crap
    SSL_library_init();
    SSL_load_error_strings();

    if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL) {
        cry(fc(ctx), "SSL_CTX_new error: %s", ssl_error());
    } else if (ctx->user_callback != NULL) {
        memset(&request_info, 0, sizeof(request_info));
        request_info.user_data = ctx->user_data;
        ctx->user_callback(MG_INIT_SSL, (struct mg_connection *) CTX,
                &request_info);
    }

    if (CTX != NULL && SSL_CTX_use_certificate_file(CTX, pem,
                SSL_FILETYPE_PEM) == 0) {
        cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error());
        return 0;
    } else if (CTX != NULL && SSL_CTX_use_PrivateKey_file(CTX, pem,
                SSL_FILETYPE_PEM) == 0) {
        cry(fc(ctx), "%s: cannot open %s: %s", NULL, pem, ssl_error());
        return 0;
    }

    if (CTX != NULL && chain != NULL &&
            SSL_CTX_use_certificate_chain_file(CTX, chain) == 0) {
        cry(fc(ctx), "%s: cannot open %s: %s", NULL, chain, ssl_error());
        return 0;
    }

    // Initialize locking callbacks, needed for thread safety.
    // http://www.openssl.org/support/faq.html#PROG1
    size = sizeof(pthread_mutex_t) * CRYPTO_num_locks();
    if ((ssl_mutexes = (pthread_mutex_t *) malloc((size_t)size)) == NULL) {
        cry(fc(ctx), "%s: cannot allocate mutexes: %s", __func__, ssl_error());
        return 0;
    }

    for (i = 0; i < CRYPTO_num_locks(); i++) {
        pthread_mutex_init(&ssl_mutexes[i], NULL);
    }

    CRYPTO_set_locking_callback(&ssl_locking_callback);
    CRYPTO_set_id_callback(&ssl_id_callback);

    // Done with everything. Save the context.
    ctx->ssl_ctx = CTX;

    return 1;
}
#endif // !NO_SSL

static int set_gpass_option(struct mg_context *ctx) {
    struct mgstat mgstat;
    const char *path = ctx->config[GLOBAL_PASSWORDS_FILE];
    return path == NULL || mg_stat(path, &mgstat) == 0;
}

static int set_acl_option(struct mg_context *ctx) {
    struct usa fake;
    return check_acl(ctx, &fake) != -1;
}

static void reset_per_request_attributes(struct mg_connection *conn) {
    struct mg_request_info *ri = &conn->request_info;

    // Reset request info attributes. DO NOT TOUCH is_ssl, remote_ip, remote_port
    if (ri->remote_user != NULL) {
        free((void *) ri->remote_user);
    }
    ri->remote_user = ri->request_method = ri->uri = ri->http_version = NULL;
    ri->num_headers = 0;
    ri->status_code = -1;

    conn->num_bytes_sent = conn->consumed_content = 0;
    conn->content_len = -1;
    conn->request_len = conn->data_len = 0;
}

static void close_socket_gracefully(SOCKET sock) {
    char buf[BUFSIZ];
    int n;

    // Send FIN to the client
    (void) shutdown(sock, SHUT_WR);
    set_non_blocking_mode(sock);

    // Read and discard pending data. If we do not do that and close the
    // socket, the data in the send buffer may be discarded. This
    // behaviour is seen on Windows, when client keeps sending data
    // when server decide to close the connection; then when client
    // does recv() it gets no data back.
    do {
        n = pull(NULL, sock, NULL, buf, sizeof(buf));
    } while (n > 0);

    // Now we know that our FIN is ACK-ed, safe to close
    (void) closesocket(sock);
}

static void close_connection(struct mg_connection *conn) {
    if (conn->ssl) {
        SSL_free(conn->ssl);
        conn->ssl = NULL;
    }

    if (conn->client.sock != INVALID_SOCKET) {
        close_socket_gracefully(conn->client.sock);
    }
}

static void discard_current_request_from_buffer(struct mg_connection *conn) {
    char *buffered;
    int buffered_len, body_len;

    buffered = conn->buf + conn->request_len;
    buffered_len = conn->data_len - conn->request_len;
    assert(buffered_len >= 0);

    if (conn->content_len == -1) {
        body_len = 0;
    } else if (conn->content_len < (int64_t) buffered_len) {
        body_len = (int) conn->content_len;
    } else {
        body_len = buffered_len;
    }

    conn->data_len -= conn->request_len + body_len;
    memmove(conn->buf, conn->buf + conn->request_len + body_len,
            (size_t) conn->data_len);
}

static int parse_url(const char *url, char *host, int *port) {
    int len;

    if (sscanf(url, "%*[htps]://%1024[^:]:%d%n", host, port, &len) == 2 ||
            sscanf(url, "%1024[^:]:%d%n", host, port, &len) == 2) {
    } else if (sscanf(url, "%*[htps]://%1024[^/]%n", host, &len) == 1) {
        *port = 80;
    } else {
        sscanf(url, "%1024[^/]%n", host, &len);
        *port = 80;
    }
    DEBUG_TRACE(("Host:%s, port:%d", host, *port));

    return len;
}

static void handle_proxy_request(struct mg_connection *conn) {
    struct mg_request_info *ri = &conn->request_info;
    char host[1025], buf[BUFSIZ];
    int port, is_ssl, len, i, n;

    DEBUG_TRACE(("URL: %s", ri->uri));
    if (conn->request_info.uri[0] == '/' ||
            (ri->uri == NULL || (len = parse_url(ri->uri, host, &port))) == 0) {
        return;
    }

    if (conn->peer == NULL) {
        is_ssl = !strcmp(ri->request_method, "CONNECT");
        if ((conn->peer = mg_connect(conn, host, port, is_ssl)) == NULL) {
            return;
        }
        conn->peer->client.is_ssl = is_ssl;
    }

    // Forward client's request to the target
    mg_printf(conn->peer, "%s %s HTTP/%s\r\n", ri->request_method, ri->uri + len,
            ri->http_version);

    // And also all headers. TODO(lsm): anonymize!
    for (i = 0; i < ri->num_headers; i++) {
        mg_printf(conn->peer, "%s: %s\r\n", ri->http_headers[i].name,
                ri->http_headers[i].value);
    }
    // End of headers, final newline
    mg_write(conn->peer, "\r\n", 2);

    // Read and forward body data if any
    if (!strcmp(ri->request_method, "POST")) {
        forward_body_data(conn, NULL, conn->peer->client.sock, conn->peer->ssl);
    }

    // Read data from the target and forward it to the client
    while ((n = pull(NULL, conn->peer->client.sock, conn->peer->ssl,
                    buf, sizeof(buf))) > 0) {
        if (mg_write(conn, buf, (size_t)n) != n) {
            break;
        }
    }

    if (!conn->peer->client.is_ssl) {
        close_connection(conn->peer);
        free(conn->peer);
        conn->peer = NULL;
    }
}

static int is_valid_uri(const char *uri) {
    // Conform to http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
    // URI can be an asterisk (*) or should start with slash.
    return (uri[0] == '/' || (uri[0] == '*' && uri[1] == '\0'));
}

static void process_new_connection(struct mg_connection *conn) {
    struct mg_request_info *ri = &conn->request_info;
    int keep_alive_enabled;
    const char *cl;

    keep_alive_enabled = !strcmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes");

    debug("开始处理连接 %d...", conn->client.sock);
    do {
        reset_per_request_attributes(conn);

        // If next request is not pipelined, read it in
        if ((conn->request_len = get_request_len(conn->buf, conn->data_len)) == 0) {
            debug("%d 读取request", conn->client.sock);
            conn->request_len = read_request(NULL, conn->client.sock, conn->ssl,
                    conn->buf, conn->buf_size, &conn->data_len);
        }
        assert(conn->data_len >= conn->request_len);
        if (conn->request_len == 0 && conn->data_len == conn->buf_size) {
            send_http_error(conn, 413, "Request Too Large", "");
            return;
        } if (conn->request_len <= 0) {
            return;  // Remote end closed the connection
        }

        // Nul-terminate the request cause parse_http_request() uses sscanf
        conn->buf[conn->request_len - 1] = '\0';
        debug("%d解析request",conn->client.sock);
        if (!parse_http_request(conn->buf, ri) ||
                (!conn->client.is_proxy && !is_valid_uri(ri->uri))) {
            // Do not put garbage in the access log, just send it back to the client
            send_http_error(conn, 400, "Bad Request",
                    "Cannot parse HTTP request: [%.*s]", conn->data_len, conn->buf);
        } else if (strcmp(ri->http_version, "1.0") &&
                strcmp(ri->http_version, "1.1")) {
            // Request seems valid, but HTTP version is strange
            send_http_error(conn, 505, "HTTP version not supported", "");
            log_access(conn);
        } else {
            // Request is valid, handle it
            cl = get_header(ri, "Content-Length");
            conn->content_len = cl == NULL ? -1 : strtoll(cl, NULL, 10);
            conn->birth_time = time(NULL);
            if (conn->client.is_proxy) {
                handle_proxy_request(conn);
            } else {
                debug("%d开始handle_request", conn->client.sock);
                handle_request(conn);
            }
            log_access(conn);
            discard_current_request_from_buffer(conn);
        }
        // conn->peer is not NULL only for SSL-ed proxy connections
    } while (conn->peer || (keep_alive_enabled && should_keep_alive(conn)));
    debug("处理连接 %d 结束...", conn->client.sock);
}

// Worker threads take accepted socket from the queue
static int consume_socket(struct mg_context *ctx, struct socket *sp) {
    (void) pthread_mutex_lock(&ctx->mutex);
    DEBUG_TRACE(("going idle"));

    // If the queue is empty, wait. We're idle at this point.
    while (ctx->sq_head == ctx->sq_tail && ctx->stop_flag == 0) {
        pthread_cond_wait(&ctx->sq_full, &ctx->mutex);
    }
    // Master thread could wake us up without putting a socket.
    // If this happens, it is time to exit.
    if (ctx->stop_flag) {
        (void) pthread_mutex_unlock(&ctx->mutex);
        return 0;
    }
    assert(ctx->sq_head > ctx->sq_tail);

    // Copy socket from the queue and increment tail
    *sp = ctx->queue[ctx->sq_tail % ARRAY_SIZE(ctx->queue)];
    ctx->sq_tail++;
    DEBUG_TRACE(("grabbed socket %d, going busy", sp->sock));

    // Wrap pointers if needed
    while (ctx->sq_tail > (int) ARRAY_SIZE(ctx->queue)) {
        ctx->sq_tail -= ARRAY_SIZE(ctx->queue);
        ctx->sq_head -= ARRAY_SIZE(ctx->queue);
    }

    (void) pthread_cond_signal(&ctx->sq_empty);
    (void) pthread_mutex_unlock(&ctx->mutex);

    return 1;
}

static void worker_thread(struct mg_context *ctx) {
    struct mg_connection *conn;
    int buf_size = atoi(ctx->config[MAX_REQUEST_SIZE]);

    conn = (struct mg_connection *) calloc(1, sizeof(*conn) + buf_size);
    conn->buf_size = buf_size;
    conn->buf = (char *) (conn + 1);
    assert(conn != NULL);

    while (ctx->stop_flag == 0 && consume_socket(ctx, &conn->client)) {
        conn->birth_time = time(NULL);
        conn->ctx = ctx;

        // Fill in IP, port info early so even if SSL setup below fails,
        // error handler would have the corresponding info.
        // Thanks to Johannes Winkelmann for the patch.
        conn->request_info.remote_port = ntohs(conn->client.rsa.u.sin.sin_port);
        memcpy(&conn->request_info.remote_ip,
                &conn->client.rsa.u.sin.sin_addr.s_addr, 4);
        conn->request_info.remote_ip = ntohl(conn->request_info.remote_ip);
        conn->request_info.is_ssl = conn->client.is_ssl;

        if (!conn->client.is_ssl ||
                (conn->client.is_ssl && sslize(conn, SSL_accept))) {
            process_new_connection(conn);
        }
        close_connection(conn);
    }
    free(conn);

    // Signal master that we're done with connection and exiting
    (void) pthread_mutex_lock(&ctx->mutex);
    ctx->num_threads--;
    (void) pthread_cond_signal(&ctx->cond);
    assert(ctx->num_threads >= 0);
    (void) pthread_mutex_unlock(&ctx->mutex);

    DEBUG_TRACE(("exiting"));
}

// Master thread adds accepted socket to a queue
static void produce_socket(struct mg_context *ctx, const struct socket *sp) {
    (void) pthread_mutex_lock(&ctx->mutex);

    // If the queue is full, wait
    while (ctx->sq_head - ctx->sq_tail >= (int) ARRAY_SIZE(ctx->queue)) {
        (void) pthread_cond_wait(&ctx->sq_empty, &ctx->mutex);
    }
    debug("current queue size, queue size, %d, %d", ctx->sq_head-ctx->sq_tail, (int)ARRAY_SIZE(ctx->queue));
    assert(ctx->sq_head - ctx->sq_tail < (int) ARRAY_SIZE(ctx->queue));

    // Copy socket to the queue and increment head
    ctx->queue[ctx->sq_head % ARRAY_SIZE(ctx->queue)] = *sp;
    ctx->sq_head++;
    DEBUG_TRACE(("queued socket %d", sp->sock));
    debug("socket %d入队列", sp->sock);

    (void) pthread_cond_signal(&ctx->sq_full);
    (void) pthread_mutex_unlock(&ctx->mutex);
}

static void accept_new_connection(const struct socket *listener,
        struct mg_context *ctx) {
    struct socket accepted;
    int allowed;

    accepted.rsa.len = sizeof(accepted.rsa.u.sin);
    accepted.lsa = listener->lsa;
    accepted.sock = accept(listener->sock, &accepted.rsa.u.sa, &accepted.rsa.len);
    if (accepted.sock != INVALID_SOCKET) {
        allowed = check_acl(ctx, &accepted.rsa);
        if (allowed) {
            // Put accepted socket structure into the queue
            DEBUG_TRACE(("accepted socket %d", accepted.sock));
            debug("accepted socket %d", accepted.sock);
            accepted.is_ssl = listener->is_ssl;
            accepted.is_proxy = listener->is_proxy;
            produce_socket(ctx, &accepted);
        } else {
            cry(fc(ctx), "%s: %s is not allowed to connect",
                    __func__, inet_ntoa(accepted.rsa.u.sin.sin_addr));
            (void) closesocket(accepted.sock);
        }
    }
}

static void master_thread(struct mg_context *ctx) {
    fd_set read_set;
    struct timeval tv;
    struct socket *sp;
    int max_fd;

    while (ctx->stop_flag == 0) {
        FD_ZERO(&read_set);
        max_fd = -1;

        // Add listening sockets to the read set
        for (sp = ctx->listening_sockets; sp != NULL; sp = sp->next) {
            add_to_set(sp->sock, &read_set, &max_fd);
        }

        tv.tv_sec = 0;
        tv.tv_usec = 200 * 1000;

        if (select(max_fd + 1, &read_set, NULL, NULL, &tv) < 0) {
#ifdef _WIN32
            // On windows, if read_set and write_set are empty,
            // select() returns "Invalid parameter" error
            // (at least on my Windows XP Pro). So in this case, we sleep here.
            sleep(1);
#endif // _WIN32
        } else {
            for (sp = ctx->listening_sockets; sp != NULL; sp = sp->next) {
                if (FD_ISSET(sp->sock, &read_set)) {
                    debug("listener %d来了一个新连接",sp->sock);
                    accept_new_connection(sp, ctx);
                }
            }
        }
    }
    DEBUG_TRACE(("stopping workers"));

    // Stop signal received: somebody called mg_stop. Quit.
    close_all_listening_sockets(ctx);

    // Wakeup workers that are waiting for connections to handle.
    pthread_cond_broadcast(&ctx->sq_full);

    // Wait until all threads finish
    (void) pthread_mutex_lock(&ctx->mutex);
    while (ctx->num_threads > 0) {
        (void) pthread_cond_wait(&ctx->cond, &ctx->mutex);
    }
    (void) pthread_mutex_unlock(&ctx->mutex);

    // All threads exited, no sync is needed. Destroy mutex and condvars
    (void) pthread_mutex_destroy(&ctx->mutex);
    (void) pthread_cond_destroy(&ctx->cond);
    (void) pthread_cond_destroy(&ctx->sq_empty);
    (void) pthread_cond_destroy(&ctx->sq_full);

    // Signal mg_stop() that we're done
    ctx->stop_flag = 2;

    DEBUG_TRACE(("exiting"));
}

static void free_context(struct mg_context *ctx) {
    int i;

    // Deallocate config parameters
    for (i = 0; i < NUM_OPTIONS; i++) {
        if (ctx->config[i] != NULL)
            free(ctx->config[i]);
    }

    // Deallocate SSL context
    if (ctx->ssl_ctx != NULL) {
        SSL_CTX_free(ctx->ssl_ctx);
    }
#ifndef NO_SSL
    if (ssl_mutexes != NULL) {
        free(ssl_mutexes);
    }
#endif // !NO_SSL

    // Deallocate context itself
    free(ctx);
}

void mg_stop(struct mg_context *ctx) {
    ctx->stop_flag = 1;

    // Wait until mg_fini() stops
    while (ctx->stop_flag != 2) {
        (void) sleep(0);
    }
    free_context(ctx);

#if defined(_WIN32) && !defined(__SYMBIAN32__)
    (void) WSACleanup();
#endif // _WIN32
}

struct mg_context *mg_start(mg_callback_t user_callback, void *user_data,
        const char **options) {
    struct mg_context *ctx;
    const char *name, *value, *default_value;
    int i;

#if defined(_WIN32) && !defined(__SYMBIAN32__)
    WSADATA data;
    WSAStartup(MAKEWORD(2,2), &data);
#endif // _WIN32

    // Allocate context and initialize reasonable general case defaults.
    // TODO(lsm): do proper error handling here.
    ctx = (struct mg_context *) calloc(1, sizeof(*ctx));
    ctx->user_callback = user_callback;
    ctx->user_data = user_data;

    while (options && (name = *options++) != NULL) {
        if ((i = get_option_index(name)) == -1) {
            cry(fc(ctx), "Invalid option: %s", name);
            free_context(ctx);
            return NULL;
        } else if ((value = *options++) == NULL) {
            cry(fc(ctx), "%s: option value cannot be NULL", name);
            free_context(ctx);
            return NULL;
        }
        ctx->config[i] = mg_strdup(value);
        DEBUG_TRACE(("[%s] -> [%s]", name, value));
    }

    // Set default value if needed
    for (i = 0; config_options[i * ENTRIES_PER_CONFIG_OPTION] != NULL; i++) {
        default_value = config_options[i * ENTRIES_PER_CONFIG_OPTION + 2];
        if (ctx->config[i] == NULL && default_value != NULL) {
            ctx->config[i] = mg_strdup(default_value);
            DEBUG_TRACE(("Setting default: [%s] -> [%s]",
                        config_options[i * ENTRIES_PER_CONFIG_OPTION + 1],
                        default_value));
        }
    }

    // NOTE(lsm): order is important here. SSL certificates must
    // be initialized before listening ports. UID must be set last.
    if (!set_gpass_option(ctx) ||
#if !defined(NO_SSL)
            !set_ssl_option(ctx) ||
#endif
            !set_ports_option(ctx) ||
#if !defined(_WIN32)
            !set_uid_option(ctx) ||
#endif
            !set_acl_option(ctx)) {
        free_context(ctx);
        return NULL;
    }

#if !defined(_WIN32) && !defined(__SYMBIAN32__)
    // Ignore SIGPIPE signal, so if browser cancels the request, it
    // won't kill the whole process.
    (void) signal(SIGPIPE, SIG_IGN);
#endif // !_WIN32

    (void) pthread_mutex_init(&ctx->mutex, NULL);
    (void) pthread_cond_init(&ctx->cond, NULL);
    (void) pthread_cond_init(&ctx->sq_empty, NULL);
    (void) pthread_cond_init(&ctx->sq_full, NULL);

    // Start master (listening) thread
    start_thread(ctx, (mg_thread_func_t) master_thread, ctx);
    debug("start master thread");

    // Start worker threads
    for (i = 0; i < atoi(ctx->config[NUM_THREADS]); i++) {
        if (start_thread(ctx, (mg_thread_func_t) worker_thread, ctx) != 0) {
            cry(fc(ctx), "Cannot start worker thread: %d", ERRNO);
        } else {
            ctx->num_threads++;
            debug("start worker thread %d",ctx->num_threads);
        }
    }

    return ctx;
}
